--[[
Process viewer, the lua version
williampan93@163.com, 2025-9
]]
rtk = require("libluartk")
local B = require("wxExt.ui_builder")
local gx = require("libgamex")
local dbg = require("wxExt.debug")

local TMainDialogDef = B.TDialog({
	sizeable = true,
	minsize = { 848, 600 },
	title = "Process viewer",
	B.THBox({
		expand = true,
		B.TLabel({ "Processes", minsize = { 70, -1 } }),
		B.TCombo({ id = "cmbProcesses", readonly = true, proportion = 1 }),
		B.TButton({ title = "刷新(&P)", id = "btnRefreshProcesses" }),
	}),
	B.TNotebook({
		expand = true,
		proportion = 1,
		B.TPanel({
			title = "Modules",
			B.TGrid({
				expand = true,
				proportion = 1,
				id = "gridModules",
				cols = { "Start", "End", "Size", "Module" },
			}),
		}),
		B.TPanel({
			title = "Memory map",
			B.TTreeList({
				expand = true,
				proportion = 1,
				id = "memMap",
				cols = { "AllocBase", "Start", "End", "Size", "Permission", "State", "Type", "Module" },
			}),
		}),
	}),
	dbg.ID_DEBUG_BAR,
})

function TMainDialogDef:updateModules(pid)
	local gridModules = self.gridModules
	gridModules:Freeze()
	local n = gridModules:GetNumberRows()
	if n > 0 then
		gridModules:DeleteRows(0, n)
	end
	local modules = gx.getProcessModules(pid)
	self.F_Env.modules = modules
	gridModules:AppendRows(#modules)
	rtk.table_ieach(modules, function(_, m)
		gridModules:SetRowLabelValue(_ - 1, string.format("%d", _))
		gridModules:SetCellValue(_ - 1, 0, string.format("%016X", m.start))
		gridModules:SetCellValue(_ - 1, 1, string.format("%016X", m.start + m.size))
		gridModules:SetCellValue(_ - 1, 2, string.format("%016X", m.size))
		gridModules:SetCellValue(_ - 1, 3, m.path)
	end)
	gridModules:AutoSizeColumns()
	gridModules:Thaw()
end

function TMainDialogDef:updateMemMap(pid)
	local memMap = self.memMap
	memMap:Freeze()
	memMap:DeleteAllItems()
	local list = gx.getProcessMemMap(pid)
	function makeGroup(list)
		local curr_base
		local groups = {}
		local g
		rtk.table_ieach(list, function(_, m)
			if not curr_base or curr_base ~= m.alloc_base then
				curr_base = m.alloc_base
				g = { m }
				g.base = m.alloc_base
				table.insert(groups, g)
			else
				table.insert(g, m)
			end
		end)
		return groups
	end
	list = makeGroup(list)
	self.F_Env.memMap = list
	local ritem = memMap:GetRootItem()
	rtk.table_ieach(list, function(_, g)
		local pitem = memMap:AppendItem(ritem, string.format("%016X", g.base))
		rtk.table_ieach(g, function(_, m)
			local item = memMap:AppendItem(pitem, "")
			memMap:SetItemText(item, 1, string.format("%016X", m.start))
			memMap:SetItemText(item, 2, string.format("%016X", m.start + m.size))
			memMap:SetItemText(item, 3, string.format("%016X", m.size))
			memMap:SetItemText(item, 4, m.permission)
			memMap:SetItemText(item, 5, m.state)
			memMap:SetItemText(item, 6, m.type)
			memMap:SetItemText(item, 7, m.path)
		end)
	end)
	memMap:Thaw()
end

function TMainDialogDef:cmbProcesses_onCombobox()
	local proc = gx.FProcesses[self.cmbProcesses.Selection + 1]
	self.F_Env.FProc = proc
	self:updateModules(proc.id)
	self:updateMemMap(proc.id)
end

function TMainDialogDef:refreshProcesses()
	local processes = gx.enumProcesses(".+")
	self.cmbProcesses:Clear()
	local windir = os.getenv("Windir")
	local procs2 = {}
	rtk.table_ieach(processes, function(_, p)
		if windir and p.exe:match("^" .. windir) then
			-- ignore system processes
			return
		end
		local str = string.format("%d(%s)", p.id, p.exe)
		self.cmbProcesses:Append(str)
		table.insert(procs2, p)
	end)
	gx.FProcesses = procs2
	rtk.printk(0, "%d processes refreshed.\n", #procs2)
end

function TMainDialogDef:btnRefreshProcesses_onClick()
	self:refreshProcesses()
end

function TMainDialogDef:doCreate(parent)
	self:refreshProcesses()
	self.gridModules:SetSelectionMode(wx.wxGrid.wxGridSelectRows)
end

local dlg = TMainDialogDef:create()
dlg:ShowModal()

